# -*- coding: utf-8 -*-

import math
import virtual_list as vl
import compute_list as cl
from zabbix import Zabbix
from gnocchi_jet import get_instance_cpu_util
from gnocchi_jet import get_instance_vcpus
'''
server_info = {
    "compute2": {"cpu": 0.15, "memory": 0.2, "network": 0.24},
    "compute3": {"cpu": 0.21, "memory": 0.22, "network": 0.14}
    }
virtual_info = {
    "2d47787c-9223-4b43-9507-e48224bf6ff5": {"cpu": 0.15, "memory": 0.2, "network": 0.24},
    "5d3f8b3d-f380-4f99-8df6-d704f8d5e5cb": {"cpu": 0.21, "memory": 0.22, "network": 0.14}
    }
server_include_virtual_info = {
    "compute2": [{"id": "2d47787c-9223-4b43-9507-e48224bf6ff5", "status": "ACTIVE"}],
    "compute3": [{"id": "f429bd89-6dfa-4b6c-aa97-c139aeb53bcc", "status": "ACTIVE"}]
    }
'''
server_info = {}
server_include_virtual_info = {}
virtual_info = {}

HOT_THRESHOLD = {"cpu": 0.90, "memory": 0.90, "network": 0.90}
COLD_THRESHOLD = {"cpu": 0.25, "memory": 0.25, "network": 0.25}
WARM_THRESHOLD = {"cpu": 0.65, "memory": 0.65, "network": 0.65}
GREEN_COMPUTING_THRESHOLD = {"cpu": 0.4, "memory": 0.4, "network": 0.4}

def get_all_server_info():
    """获取系统所有物理机的资源利用率信息。
    
    获取系统所有正在运行的物理机的资源利用率信息，包括CPU、内存和带宽的利用率。

    参数：
        无

    返回值：
        无，该函数直接修改全局变量server_info的值。
        server_info为一个字典类型，大体结构如下。
        server_info = {
            "compute2": {"cpu": 0.15, "memory": 0.2, "network": 0.24},
            "compute3": {"cpu": 0.21, "memory": 0.22, "network": 0.14}
        }
    """
    global server_info
    #每次获取物理机负载信息之前都要初始化字典，保证信息都是重新获取，和以前没有关系。
    server_info = {}
    result = Zabbix()
    compute_services = cl.get_compute_service_list()
    for compute_service in compute_services:
        if compute_service["status"] == "enabled" and compute_service["state"] == "up":
            cpu_result = result.getCpu(compute_service["host"])
            memory_result = result.get_memory_use(compute_service["host"])
            compute_network_result = result.getComputeTraffic(compute_service["host"])
            server_info[compute_service["host"]] = {
                "cpu": 1-cpu_result/100,
                "memory": memory_result,
                "network": float(compute_network_result)/2000000000
            }

def get_all_server_include_virtual_info():
    """获取系统所有物理机中运行的虚拟机信息。
    
    获取系统所有正在运行的物理机中虚拟机的信息，包括虚拟机的ID和状态。

    参数：
        无

    返回值：
        无，该函数直接修改全局变量server_include_virtual_info的值。
        server_include_virtual_info为字典类型，结构大体如下。
        server_include_virtual_info = {
            "compute2": [{"id": "2d47787c-9223-4b43-9507-e48224bf6ff5", "status": "ACTIVE"}],
            "compute3": [{"id": "f429bd89-6dfa-4b6c-aa97-c139aeb53bcc", "status": "ACTIVE"}]
        }
    """
    global server_include_virtual_info
    #每次获取物理机对应的虚拟机信息之前都要初始化字典，保证信息都是重新获取，和以前没有关系。
    server_include_virtual_info = {}
    virtual_in_compute_list = []
    compute_services = cl.get_compute_service_list()
    for compute_service in compute_services:
        if compute_service["status"] == "enabled" and compute_service["state"] == "up":
            virtuals = vl.get_server_virtual(compute_service["host"])
            for virtual in virtuals:
                if virtual["status"] == "ACTIVE":
                    virtual_in_compute_list.append(virtual)
            server_include_virtual_info[compute_service["host"]] = virtual_in_compute_list
            virtual_in_compute_list = []

def get_all_virtual_info():
    """获取系统所有的虚拟机资源利用率。
    
    获取系统所有的虚拟机的资源利用率，包括CPU、内存和网络带宽（带宽暂时不获取）。

    参数：
        无

    返回值：
        无，该函数直接给修改全局变量virtual_info的值。
        virtual_info是字典类型，结构大体如下。
        virtual_info = {
            "2d47787c-9223-4b43-9507-e48224bf6ff5": {"cpu": 0.15, "memory": 0.2, "network": 0.24},
            "5d3f8b3d-f380-4f99-8df6-d704f8d5e5cb": {"cpu": 0.21, "memory": 0.22, "network": 0.14}
        }
    """
    global virtual_info
    global server_include_virtual_info
    #每次获取虚拟机负载信息之前都要初始化字典，保证信息都是重新获取，和以前没有关系。
    virtual_info = {}
    for server in server_include_virtual_info.keys():
        virtuals = server_include_virtual_info[server]
        for virtual in virtuals:
            try:
                cpu_result = get_instance_cpu_util(virtual["id"])
                vcpus_result = get_instance_vcpus(virtual["id"])
            except(IndexError):
                cpu_result = 0.0
                vcpus_result = 0.0 
            virtual_info[virtual["id"]] = {
                "cpu": float(cpu_result)/100 * float(vcpus_result)/8,
                "memory": 0,
                "network": 0
            }

def get_server_skewness(cpu_utilization, memory_utilization, network_utilization):
    """计算物理机的资源使用率偏移度。
    
    根据物理主机的cpu，内存和网络吞吐计算资源利用率偏移度。

    参数：
        cpu_utilization: cpu使用率。 
        memory_utilization: 内存使用率。
        network_utilization: 网络吞吐量。

    返回值：
        资源利用率偏移度。
    """
    ave_utilization = (cpu_utilization + memory_utilization + network_utilization) / 3
    if ave_utilization == 0.0:
        skewness = 0.0
    else:
        cpu_dev = cpu_utilization / ave_utilization - 1
        memory_dev = memory_utilization / ave_utilization - 1
        network_dev = network_utilization / ave_utilization - 1
        num_dev = (math.pow(cpu_dev, 2) + math.pow(memory_dev, 2) + math.pow(network_dev, 2))
        skewness = math.sqrt(num_dev)
    return skewness

def get_server_temperature(cpu_utilization, memory_utilization, network_utilization):
    """计算物理机的温度。
    
    根据物理主机的cpu，内存和网络吞吐计算其温度。

    参数：
        cpu_utilization: cpu使用率。 
        memory_utilization: 内存使用率。
        network_utilization: 网络吞吐量。

    返回值：
        物理主机温度。
    """
    temperature = 0
    global HOT_THRESHOLD
    if cpu_utilization > HOT_THRESHOLD["cpu"]:
        temperature += math.pow((cpu_utilization - HOT_THRESHOLD["cpu"]), 2)
    if memory_utilization > HOT_THRESHOLD["memory"]:
        temperature += math.pow((memory_utilization - HOT_THRESHOLD["memory"]), 2)
    if network_utilization > HOT_THRESHOLD["network"]:
        temperature += math.pow((network_utilization - HOT_THRESHOLD["network"]), 2)
    return temperature

def pretreat():
    """预处理函数。
    
    计算所有物理主机的温度和资源利用率偏移度。

    参数：
        无

    返回值：
        无
    """
    global server_info
    global virtual_info
    global server_include_virtual_info
    
    get_all_server_info()
    get_all_server_include_virtual_info()
    get_all_virtual_info()
    for server_name in server_info.keys():
        server_info[server_name]["skewness"] = get_server_skewness(server_info[server_name]["cpu"],
            server_info[server_name]["memory"], server_info[server_name]["network"])
        server_info[server_name]["temperature"] = get_server_temperature(server_info[server_name]["cpu"],
            server_info[server_name]["memory"], server_info[server_name]["network"])
    server_info = server_info
    virtual_info = virtual_info
    server_include_virtual_info = server_include_virtual_info

def update_server_info(source_server, destination_server, virtual_id, virtual_id_status):
    """虚拟机迁移物理主机利用率更新函数。
    
    虚拟机迁移时，更新其源物理主机和目的物理主机的信息。

    参数：
        source_server: 源物理主机名称。
        destination_server: 目的物理主机名称。
        virtual_id: 要迁移的虚拟机id。
        virtual_id_status: 要迁移的虚拟机状态。

    返回值：
        无
    """
    global server_info
    global virtual_info
    global server_include_virtual_info
    #更新source_server(正在处理的物理机)的信息
    server_info[source_server]["cpu"] -= virtual_info[virtual_id]["cpu"]
    server_info[source_server]["memory"] -= virtual_info[virtual_id]["memory"]
    server_info[source_server]["network"] -= virtual_info[virtual_id]["network"]
    server_info[source_server]["skewness"] = get_server_skewness(
        server_info[source_server]["cpu"], server_info[source_server]["memory"],
        server_info[source_server]["network"])
    server_info[source_server]["temperature"] = get_server_temperature(
        server_info[source_server]["cpu"], server_info[source_server]["memory"],
        server_info[source_server]["network"])
    server_include_virtual_info[source_server].remove({"id": virtual_id, "status": virtual_id_status})

    #更新destination_server(虚拟机迁往的目的主机)信息
    server_info[destination_server]["cpu"] += virtual_info[virtual_id]["cpu"]
    server_info[destination_server]["memory"] += virtual_info[virtual_id]["memory"]
    server_info[destination_server]["network"] += virtual_info[virtual_id]["network"]
    server_info[destination_server]["skewness"] = get_server_skewness(
        server_info[destination_server]["cpu"], server_info[destination_server]["memory"],
        server_info[destination_server]["network"])
    server_info[destination_server]["temperature"] = get_server_temperature(
        server_info[destination_server]["cpu"], server_info[destination_server]["memory"],
        server_info[destination_server]["network"])
    server_include_virtual_info[destination_server].append({"id": virtual_id, "status": virtual_id_status})
 

def solve_hotspot():
    """热点处理函数。
    
    计算系统中处于热点的物理机，计算热点物理机需要外迁的虚拟机，以及其状态和要迁移的目的主机。

    参数：
        无

    返回值：
        需要迁移的虚拟机列表。
        返回格式为列表，列表中的元素为字典。字典中"id"标识虚拟机的标识，"status"表示虚拟机的状态，
        "source"表示虚拟所在的物理主机，"destination"表示虚拟机要迁往的目的主机。
    """
    global server_info
    global virtual_info
    global server_include_virtual_info
    #virtual_result 为处理热点而需要被迁移的虚拟机列表
    virtual_result = []
    server_hot_temperature_help = 100000
    server_destination_skewness_help = 10000
    server_hots = []
    server_hots_name = []
    for server_name in server_info.keys():
        if server_info[server_name]["temperature"] > 0:
            server_hots.append({"id": server_name, "temperature":server_info[server_name]["temperature"]})
            server_hots_name.append(server_name)
    server_hots = sorted(server_hots, key=lambda bsd: bsd["temperature"], reverse=True)
    for server_hot in server_hots:
        #首先计算热点server_hot的哪个虚拟机被迁出，只迁出一个虚拟机
        server_hot_name = server_hot["id"]
        virtual_in_thehots = server_include_virtual_info[server_hot_name]
        virtual_id_result = ''
        for virtual_in_thehot in virtual_in_thehots:
            virtual_in_thehot_id = virtual_in_thehot["id"]
            virtual_in_thehot_id_status = virtual_in_thehot["status"]
            after_migrate_temperature = get_server_temperature(
                server_info[server_hot_name]["cpu"] - virtual_info[virtual_in_thehot_id]["cpu"],
                server_info[server_hot_name]["memory"] - virtual_info[virtual_in_thehot_id]["memory"],
                server_info[server_hot_name]["network"] - virtual_info[virtual_in_thehot_id]["network"])
            if after_migrate_temperature > 0:
                if after_migrate_temperature < server_hot_temperature_help:
                    virtual_id_result = virtual_in_thehot_id
                    virtual_id_result_status = virtual_in_thehot_id_status
                    server_hot_temperature_help = after_migrate_temperature
            else:
                virtual_id_result = virtual_in_thehot_id
                virtual_id_result_status = virtual_in_thehot_id_status
                break
        server_hot_temperature_help = 100000
        #virtual_id_result为确认的在server_hot上要迁出的虚拟机id，
        #virtual_id_result_status为该虚拟机的状态
        #接下来计算该虚拟机迁移的目的物理机
        #server_destination_name_result为虚拟机迁移的目的主机名称
        server_destination_name_result = ''
        if virtual_id_result == '':
            continue
        for server_destination_name in server_info.keys():
            if server_destination_name not in server_hots_name:
                after_migrate_temperature = get_server_temperature(
                    server_info[server_destination_name]["cpu"] + virtual_info[virtual_id_result]["cpu"],
                    server_info[server_destination_name]["memory"] + virtual_info[virtual_id_result]["memory"],
                    server_info[server_destination_name]["network"] + virtual_info[virtual_id_result]["network"])
                if after_migrate_temperature > 0:
                    continue
                else:
                    after_migrate_skewness = get_server_skewness(
                        server_info[server_destination_name]["cpu"] + virtual_info[virtual_id_result]["cpu"],
                        server_info[server_destination_name]["memory"] + virtual_info[virtual_id_result]["memory"],
                        server_info[server_destination_name]["network"] + virtual_info[virtual_id_result]["network"])
                    migrate_skewness_change = after_migrate_skewness - server_info[server_destination_name]["skewness"]
                    if migrate_skewness_change < server_destination_skewness_help:
                        server_destination_name_result = server_destination_name
                        server_destination_skewness_help = migrate_skewness_change
        server_destination_skewness_help = 100000
        if server_destination_name_result == '':
            continue
        else:
            #将待迁移的虚拟机信息(包括该虚拟机的id，状态，源物理主机和迁移的目的物理机名称)加入到virtual_result中
            virtual_result.append({"id": virtual_id_result, "status": virtual_id_result_status,
                "source": server_hot_name, "destination": server_destination_name_result})
            #更新信息 
            update_server_info(server_hot_name, server_destination_name_result, virtual_id_result, virtual_id_result_status)
    return virtual_result

def solve_green_computing():
    """绿色节能处理函数。
    
    计算系统的平均资源利用率，判断是否冷点。如果可以处理冷点，则计算冷点列表，然后根据规则将冷点内的虚拟机外迁。

    参数：
        无

    返回值：
        需要迁移的虚拟机列表和需要关闭的物理机列表。
        返回格式为元组。
        元组内第一个元素为列表，列表中的元素为字典。字典中"id"标识虚拟机的标识，"status"表示虚拟机的状态，
        "source"表示虚拟所在的物理主机，"destination"表示虚拟机要迁往的目的主机。
        元组中第二元素为列表，列表位需要关闭的物理机列表，列表中的元素为字符串。
    """
    global server_info
    global virtual_info
    global server_include_virtual_info
    global COLD_THRESHOLD
    global WARM_THRESHOLD
    global GREEN_COMPUTING_THRESHOLD
    #virtual_result是处理green_computing需要迁移的虚拟机列表
    virtual_result = []
    #在判断一个冷点的虚拟机是否可以全部迁出时使用
    virtual_result_temp = []
    #server_shutdown_result = []是全部虚拟机迁出后，需要关机的物理机列表
    server_shutdown_result = []
    server_destination_skewness_help = 100000
    server_colds = []
    server_ave_cpu = 0
    server_ave_memory = 0
    server_ave_network = 0
    #计算所有物理机的平均资源利用率
    for server_name in server_info.keys():
       server_ave_cpu += server_info[server_name]["cpu"]
    server_ave_cpu /= len(server_info.keys())
    for server_name in server_info.keys():
       server_ave_memory += server_info[server_name]["memory"]
    server_ave_memory /= len(server_info.keys())
    for server_name in server_info.keys():
       server_ave_network += server_info[server_name]["network"]
    server_ave_network /= len(server_info.keys())
    #判断所有物理机的平均资源使用率是否都低于GREEN_COMPUTING_THRESHOLD,如果条件符合，则处理冷点
    if (server_ave_cpu < GREEN_COMPUTING_THRESHOLD["cpu"] and
        server_ave_memory < GREEN_COMPUTING_THRESHOLD["memory"] and
        server_ave_network < GREEN_COMPUTING_THRESHOLD["network"]):
        #计算冷点列表，并且按照内存升序排序
        for server_name in server_info.keys():
            if (server_info[server_name]["cpu"] < COLD_THRESHOLD["cpu"] and
                server_info[server_name]["memory"] < COLD_THRESHOLD["memory"] and
                server_info[server_name]["network"] < COLD_THRESHOLD["network"]):
                server_colds.append({"name": server_name, "memory": server_info[server_name]["memory"]})
        server_colds = sorted(server_colds, key=lambda bsd: bsd["memory"], reverse=False)
        #开始处理冷点 
        for server_cold in server_colds:
            server_cold_name = server_cold["name"]
            #由于虚拟机的迁移，在冷点列表内的物理机可能已经不再是冷点，所以要重新进行判断
            if (server_info[server_cold_name]["cpu"] < COLD_THRESHOLD["cpu"] and
                server_info[server_cold_name]["memory"] < COLD_THRESHOLD["memory"] and
                server_info[server_cold_name]["network"] < COLD_THRESHOLD["network"]):
                print server_cold_name
                virtual_in_thecolds = server_include_virtual_info[server_cold_name]
                virtual_in_thecolds_cnt = len(virtual_in_thecolds)
                cnt = 0
                for virtual_in_thecold in virtual_in_thecolds:
                    virtual_in_thecold_id = virtual_in_thecold["id"]
                    virtual_in_thecold_id_status = virtual_in_thecold["status"]
                    destination_server_name_result = ''
                    for destination_server_name in server_info.keys():
                        if destination_server_name != server_cold_name and destination_server_name not in server_shutdown_result:
                            if (server_info[destination_server_name]["cpu"] + virtual_info[virtual_in_thecold_id]["cpu"] > WARM_THRESHOLD["cpu"] or
                                server_info[destination_server_name]["memory"] + virtual_info[virtual_in_thecold_id]["memory"] > WARM_THRESHOLD["memory"] or
                                server_info[destination_server_name]["network"] + virtual_info[virtual_in_thecold_id]["network"] > WARM_THRESHOLD["network"]):
                                continue
                            else:
                                after_migrate_skewness = get_server_skewness(
                                    server_info[destination_server_name]["cpu"] + virtual_info[virtual_in_thecold_id]["cpu"],
                                    server_info[destination_server_name]["memory"] + virtual_info[virtual_in_thecold_id]["memory"],
                                    server_info[destination_server_name]["network"] + virtual_info[virtual_in_thecold_id]["network"])
                                migrate_skewness_change = after_migrate_skewness - server_info[destination_server_name]["skewness"]
                                #根据优先选择非冷点作为迁移目的物理机的原则，当目的物理机为冷点时，将skewness的变化值调高，降低优先级(优先选择skewness低的)
                                if (server_info[destination_server_name]["cpu"] < COLD_THRESHOLD["cpu"] and
                                    server_info[destination_server_name]["memory"] < COLD_THRESHOLD["memory"] and
                                    server_info[destination_server_name]["network"] < COLD_THRESHOLD["network"]):
                                    migrate_skewness_change += 1000
                                if migrate_skewness_change < server_destination_skewness_help:
                                    server_destination_skewness_help = migrate_skewness_change
                                    destination_server_name_result = destination_server_name
                    server_destination_skewness_help = 100000
                    if destination_server_name_result == '':
                       break
                    else:
                        cnt += 1
                        #找到正在处理的虚拟机符合条件的迁移目的物理机，将该虚拟机id，状态，源物理机和迁移目的物理机名称加入到临时列表中
                        #flag = 0
                        #for virtual_result_temp_info in virtual_result_temp:
                        #    if virtual_in_thecold_id == virtual_result_temp_info["id"]:
                        #        print "m"
                        #        flag = 1
                        #        virtual_result_temp_info["destination"] = destination_server_name_result
                        #        break
                        #if flag == 0:
                        #    virtual_result_temp.append({"id": virtual_in_thecold_id, "status": virtual_in_thecold_id_status,
                        #        "source": server_cold_name, "destination": destination_server_name_result})
                        #update_server_info(server_cold_name, destination_server_name_result, virtual_in_thecold_id, virtual_in_thecold_id_status)
                        virtual_result_temp.append({"id": virtual_in_thecold_id, "status": virtual_in_thecold_id_status,
                                "source": server_cold_name, "destination": destination_server_name_result})
                #表明正在处理的冷点的所有虚拟机都找到了符合条件的目的物理机
                if virtual_in_thecolds_cnt == cnt:
                    for virtual_result_temp_info in virtual_result_temp:
                        flag = 0
                        for virtual_result_info in virtual_result:
                            if virtual_result_info["id"] == virtual_result_temp_info["id"]:
                                virtual_result_info["destination"] = virtual_result_temp_info["destination"]
                                flag = 1
                                break
                        if flag == 0:
                            virtual_result.append(virtual_result_temp_info)
                        update_server_info(server_cold_name, virtual_result_temp_info["destination"], virtual_result_temp_info["id"], virtual_result_temp_info["status"])
                    server_shutdown_result.append(server_cold_name)
                    #删除所有虚拟机即将被迁走的物理机信息，因为其即将被关闭
                    server_info.pop(server_cold_name)
                    server_include_virtual_info.pop(server_cold_name)
                #表明正在处理的冷点不是所有的虚拟机都找到了符合条件的目的物理机
                #else:
                #    for virtual_result_info in virtual_result_temp:
                #        update_server_info(virtual_result_info["destination"], server_cold_name, virtual_result_info["id"], virtual_result_info["status"])
                virtual_result_temp = []
    return (virtual_result, server_shutdown_result)                

def consolidate_movement(hot_movement_list, green_compute_movement_list):
    """合并热点处理和冷点处理中的需要迁移的虚拟机列表。
    
    如果在热点中计算到将一个虚拟机m从物理机A迁移到物理机B，在冷点处理中计算到将虚拟机m将B迁移到C，
    则将两者合并，直接返回虚拟机m从A迁移到C。

    参数：
        hot_movement_list: 热点处理中计算得到的虚拟机迁移列表。
        green_compute_movement_list: 绿色节能中计算得到的虚拟机迁移列表。

    返回值：
        本次自动迁移算法计算得到的总虚拟机迁移列表。
    """
    for hot_movement in hot_movement_list:
        hot_movement_id = hot_movement["id"]
        for green_compute_movement in green_compute_movement_list:
            green_compute_movement_id = green_compute_movement["id"]
            if hot_movement_id == green_compute_movement_id:
                hot_movement["destination"] = green_compute_movement["destination"]
                green_compute_movement_list.remove(green_compute_movement)
                break
    movement_list = hot_movement_list + green_compute_movement_list
    return movement_list
         
def main():
    global server_info
    global virtual_info
    global server_include_virtual_info
    pretreat()
    print server_info
    print server_include_virtual_info
    print virtual_info
    hot_movement_list = solve_hotspot()
    (green_compute_movement_list, compute_shutdown_list) = solve_green_computing()
    movement_list = consolidate_movement(hot_movement_list, green_compute_movement_list)
    print movement_list
    print compute_shutdown_list
if __name__ == '__main__':
    main()  
